package com.cennavi.mineplay.mineplay_data.utils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateList;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryComponentFilter;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;

public class LineMergeImp {
    private List<LineString> mergedLineStrings = null;
    private GeometryFactory factory = null;
    private Map<Point, Integer> edgePoint = null;


    public LineMergeImp() {
        super();
        this.mergedLineStrings = new ArrayList<LineString>();
        this.edgePoint = new HashMap<Point, Integer>();
    }

    public static boolean hasRepeatedPoints(Coordinate[] coord) {
        for (int i = 1; i < coord.length; i++) {
            if (coord[i - 1].equals(coord[i])) {
                return true;
            }
        }
        return false;
    }

    public static Coordinate[] removeRepeatedPoints(Coordinate[] coord) {
        if (!hasRepeatedPoints(coord))
            return coord;
        CoordinateList coordList = new CoordinateList(coord, false);
        return coordList.toCoordinateArray();
    }


    public void add(Geometry geometry) {
        geometry.apply(new GeometryComponentFilter() {
            public void filter(Geometry component) {
                if (component instanceof LineString) {
                    add((LineString) component);
                }
            }
        });
    }

    public List<LineString> getMergedLineStrings() {
        return mergedLineStrings;
    }

    public void clear() {
        mergedLineStrings = null;
        edgePoint = null;
    }

    public void add(LineString lineString) {

        if (factory == null) {
            factory = lineString.getFactory();
        }
        if (mergedLineStrings == null) {
            mergedLineStrings = new ArrayList<LineString>();
        }
        if (edgePoint == null) {
            edgePoint = new HashMap<Point, Integer>();
        }
        if (mergedLineStrings.size() == 0) {
            mergedLineStrings.add(lineString);
            return;
        }
        edgePoint.clear();
        int i = 0;
        for (Iterator<LineString> iterator = mergedLineStrings.iterator(); iterator.hasNext();) {
            LineString next = iterator.next();
            edgePoint.put(next.getStartPoint(), i);
            edgePoint.put(next.getEndPoint(), i + 1);
            i += 2;

        }
        // LineString lastLine = mergedLineStrings.get(mergedLineStrings.size() - 1);
        // if (lastLine.getEndPoint().equalsExact(lineString.getStartPoint())) {
        // ArrayList<Coordinate> mergeCoordList = new ArrayList<Coordinate>();
        // mergeCoordList.addAll(Arrays.asList(lastLine.getCoordinates()));
        // mergeCoordList.addAll(Arrays.asList(lineString.getCoordinates()));
        // Coordinate[] array = mergeCoordList.toArray(new Coordinate[] {});
        // array = removeRepeatedPoints(array);
        // mergedLineStrings.remove(mergedLineStrings.size() - 1);
        // mergedLineStrings.add(factory.createLineString(array));
        // return;
        // } else {
        // mergedLineStrings.add(lineString);
        // }
        Set<Point> keySet = edgePoint.keySet();
        Point startPoint = lineString.getStartPoint();
        Point endPoint = lineString.getEndPoint();
        if (keySet.contains(startPoint)) {
            int index = edgePoint.get(startPoint);
            if (index % 2 != 0) {
                LineString selectLine = mergedLineStrings.get(index - (index / 2 + 1));
                ArrayList<Coordinate> mergeCoordList = new ArrayList<Coordinate>();
                mergeCoordList.addAll(Arrays.asList(selectLine.getCoordinates()));
                mergeCoordList.addAll(Arrays.asList(lineString.getCoordinates()));
                Coordinate[] array = mergeCoordList.toArray(new Coordinate[] {});
                array = removeRepeatedPoints(array);
                mergedLineStrings.remove(index - (index / 2 + 1));
                mergedLineStrings.add(factory.createLineString(array));
            } else {
                LineString selectLine = mergedLineStrings.get(index - index / 2);
                if (!(selectLine.covers(lineString) || selectLine.coveredBy(lineString))) {
                    ArrayList<Coordinate> mergeCoordList = new ArrayList<Coordinate>();
                    List<Coordinate> asList = Arrays.asList(lineString.getCoordinates());
                    Collections.reverse(asList);
                    mergeCoordList.addAll(asList);
                    mergeCoordList.addAll(Arrays.asList(selectLine.getCoordinates()));
                    Coordinate[] array = mergeCoordList.toArray(new Coordinate[] {});
                    array = removeRepeatedPoints(array);
                    mergedLineStrings.remove(index - index / 2);
                    mergedLineStrings.add(factory.createLineString(array));
                }
            }
        } else if (keySet.contains(endPoint)) {
            int index = edgePoint.get(endPoint);
            if (index % 2 == 0) {
                LineString selectLine = mergedLineStrings.get(index - index / 2);
                ArrayList<Coordinate> mergeCoordList = new ArrayList<Coordinate>();
                mergeCoordList.addAll(Arrays.asList(lineString.getCoordinates()));
                mergeCoordList.addAll(Arrays.asList(selectLine.getCoordinates()));
                Coordinate[] array = mergeCoordList.toArray(new Coordinate[] {});
                array = removeRepeatedPoints(array);
                mergedLineStrings.remove(index - index / 2);
                mergedLineStrings.add(factory.createLineString(array));
            } else {
                LineString selectLine = mergedLineStrings.get(index - (index / 2 + 1));
                if (!(selectLine.covers(lineString) || selectLine.coveredBy(lineString))) {
                    ArrayList<Coordinate> mergeCoordList = new ArrayList<Coordinate>();
                    mergeCoordList.addAll(Arrays.asList(selectLine.getCoordinates()));
                    List<Coordinate> asList = Arrays.asList(lineString.getCoordinates());
                    Collections.reverse(asList);
                    mergeCoordList.addAll(asList);
                    Coordinate[] array = mergeCoordList.toArray(new Coordinate[] {});
                    array = removeRepeatedPoints(array);
                    mergedLineStrings.remove(index - (index / 2 + 1));
                    mergedLineStrings.add(factory.createLineString(array));
                }
            }
        } else {
            mergedLineStrings.add(lineString);
        }

    }
}
